package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.config.VodProperties;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import com.tencentcloudapi.common.AbstractModel;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.vod.v20180717.VodClient;
import com.tencentcloudapi.vod.v20180717.models.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

	@Autowired
	private TrackInfoMapper trackInfoMapper;

    @Autowired
    private VodProperties vodProperties;

    @Autowired
    private AlbumInfoMapper albumInfoMapper;
    @Autowired
    private TrackStatMapper trackStatMapper;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Override
    public Map<String, Object> uploadTrack(MultipartFile file) {



        VodUploadResponse response=null;
        // 1.初始化客户端
        /*
        SecretId:AKIDGOL4au8FAyb1GJAsjou5B5mFUUFn7C2l
        SecretKey:byAAyd6NbdVEm9NQGyUu8B0b4KOGvNdh
        */
        VodUploadClient client = new VodUploadClient(vodProperties.getSecretId(), vodProperties.getSecretKey());

        //2.构造一个上传的请求对象
        VodUploadRequest request = new VodUploadRequest();
        request.setSubAppId(vodProperties.getAppId().longValue());
        request.setStorageRegion(vodProperties.getRegion());
        request.setConcurrentUploadNumber(5); //指定处理分片任务的线程数(vod默认会对大文件上传)
        //2.1指定上传文件(音频)的地址
        String temFilepPath = UploadFileUtil.uploadTempPath(vodProperties.getTempPath(), file);
        request.setMediaFilePath(temFilepPath);

        //3.调用上传方法，传入接入点地域(默认vod的服务器用的是广州)及上传请求。
        try {
             response = client.upload(vodProperties.getRegion(), request);
            log.info("Upload FileId = {}", response.getFileId());
        } catch (Exception e) {
            // 业务方进行异常处理
            log.error("上传出错", e.getMessage());
            throw new GuiguException(201,"上传失败");
        }

        HashMap<String, Object> map = new HashMap<>();
        map.put("mediaFileId",response.getFileId()); //上传到vod的文件id给我
        map.put("mediaUrl",response.getMediaUrl()); //上传到vod的文件url给我

        return map;
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveTrackInfo(TrackInfoVo trackInfoVo) {

        TrackInfoServiceImpl ProxyObject = (TrackInfoServiceImpl) AopContext.currentProxy();
        //1保存声音信息,基本信息
        TrackInfo trackInfo = new TrackInfo();
        BeanUtils.copyProperties(trackInfoVo,trackInfo);
        trackInfo.setUserId(AuthContextHolder.getUserId());
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);

        //获取m文件的媒体信息
        TrackMediaInfoVo trackMediaInfoVo = getMediaInfo(trackInfoVo.getMediaFileId());
        if (trackMediaInfoVo!=null){
            trackInfo.setMediaSize(trackMediaInfoVo.getSize());
            trackInfo.setMediaDuration(new BigDecimal(trackMediaInfoVo.getDuration()+""));
            trackInfo.setMediaType(trackMediaInfoVo.getType());
            trackInfo.setMediaUrl(trackMediaInfoVo.getMediaUrl());
        }

        //判断这个声音的对应专辑下有没有声音,如果有,当前声音是之前声音的最大序列号+1,没有就是1
        TrackInfo trackInfoFromDb = trackInfoMapper.selectOne(new LambdaQueryWrapper<TrackInfo>()
                .eq(TrackInfo::getAlbumId, trackInfoVo.getAlbumId())
                .orderByDesc(TrackInfo::getOrderNum)
                .last("limit 1")
        );
        Integer orderNum = trackInfoFromDb==null?1:trackInfoFromDb.getOrderNum()+1;
        trackInfo.setOrderNum(orderNum);
        trackInfoMapper.insert(trackInfo);
        //初始化声音统计表
        ProxyObject.initTrackStat(trackInfo.getId());
        //修改专辑对应的声音集数
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfoVo.getAlbumId());
        if (albumInfo==null){
            throw new GuiguException(201,"专辑不存在");
        }
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount()+1);
        albumInfoMapper.updateById(albumInfo);

    }

    @Override
    public IPage<TrackListVo> findUserTrackPage(IPage<TrackListVo> trackListVoIPage, TrackInfoQuery trackInfoQuery) {

        return trackInfoMapper.findUserTrackPage(trackListVoIPage,trackInfoQuery);
    }

    /**
     * 修改声音
     * @param trackInfoVo
     * @param trackId
     * @param userId
     */
    @Override
    public void updateTrackInfo(TrackInfoVo trackInfoVo, Long trackId, Long userId) {
        //1.查询老的声音数据
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        if (trackInfo==null){
            throw new GuiguException(201,"声音不存在");
        }
        String oldmediaFileId = trackInfo.getMediaFileId();
        String newmediaFileId = trackInfoVo.getMediaFileId();
        if (!oldmediaFileId.equals(newmediaFileId)&&!StringUtils.isEmpty(newmediaFileId)){
            //如果文件id不一样，需要删除老文件，上传新文件
            BeanUtils.copyProperties(trackInfoVo,trackInfo);
            trackInfo.setUserId(userId);
            //获取文件的媒体信息
            TrackMediaInfoVo trackMediaInfoVo = getMediaInfo(trackInfo.getMediaFileId());
            if (trackMediaInfoVo!=null){
                trackInfo.setMediaSize(trackMediaInfoVo.getSize());
                trackInfo.setMediaDuration(new BigDecimal(trackMediaInfoVo.getDuration()));
                trackInfo.setMediaType(trackMediaInfoVo.getType());
                trackInfo.setMediaUrl(trackMediaInfoVo.getMediaUrl());
            }

        }
        trackInfoMapper.updateById(trackInfo);
    }

    /**
     * 删除声音
     * @param userId
     * @param trackId
     */
    @Override
    public void removeTrackInfo(Long userId, Long trackId) {
        //1.删除声音的基本信息
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        if (trackInfo==null){
            throw new GuiguException(201,"声音不存在");
        }
        Long albumId = trackInfo.getAlbumId();
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        if (albumInfo==null){
            throw new GuiguException(201,"专辑不存在");
        }
        trackInfoMapper.deleteById(trackId);

        //2.删除声音的统计信息
        int delete = trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId, trackId));
        if (delete<=0){
            throw new GuiguException(201,"删除失败");
        }
        //3.删除vod中上传的声音(临时目录下的声音文件可以选择删除)
        removeVodFiled(trackInfo.getMediaFileId());

        //4.修改删除声音对应的专辑的声音数量
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount()-1);
        albumInfoMapper.updateById(albumInfo);


    }

    @Override
    public IPage<AlbumTrackListVo> findAlbumTrackPage(IPage<AlbumTrackListVo> albumTrackListVoPage, Long albumId) {

        //0.创建一个返回值
        IPage<AlbumTrackListVo>  result = null;
        //1.获取用户id
        Long userId = AuthContextHolder.getUserId();

        //2.根据用户id获取专辑的下的分页后的所有声音列表
        IPage<AlbumTrackListVo> albumTrackListVoIPage=trackInfoMapper.findAlbumTrackPage(albumTrackListVoPage,albumId);

        //3.查询专辑的基本信息
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        if (albumInfo==null){
            throw new GuiguException(201,"专辑不存在");
        }
        //3.1根据专辑的基本信息获取专辑的付费类型
        String payType = albumInfo.getPayType();
        //3.2 判断专辑是否是免费专辑,如果不是免费的则需要获取专辑的免费集数
        Integer tracksForFree = albumInfo.getTracksForFree();
        //3.3获取专辑的价格类型(单集和整个专辑,提前查出来为了给后面校验是否买过该专辑做准备)
        String PayType = albumInfo.getPayType();
        switch (PayType){
            case "0101":
                result = albumTrackListVoIPage;
                break;
            case "0102":
                result=dealVipTypeAlbum(userId,albumId,tracksForFree,albumTrackListVoIPage,payType);

                break;
            case "0103":
                result=dealNeedTypeAlbum(userId,albumId,tracksForFree,albumTrackListVoIPage,payType);
        }
        return result;
    }


    /**
     * 获取声音的统计信息
     * @param trackId
     * @return
     */
    @Override
    public TrackStatVo getTrackStatVo(Long trackId) {
        return trackStatMapper.getTrackStatVo(trackId);
    }

    @Override
    public List<TrackListVo> getTrackListVo(List<Long> trackIds) {
        List<TrackInfo> trackInfos = trackInfoMapper.selectBatchIds(trackIds);
        List<TrackListVo> collect = trackInfos.stream().map(trackInfo -> {
            TrackListVo trackListVo = new TrackListVo();
            BeanUtils.copyProperties(trackInfo, trackListVo);
            trackListVo.setTrackId(trackInfo.getId());
            return trackListVo;
        }).collect(Collectors.toList());
        return collect;
    }


    /**
     * 分集展示用户要购买的声音的列表
     * @param trackId
     * @return
     */
    @Override
    public List<Map<String, Object>> findUserTrackPaidList(Long trackId) {

        ArrayList<Map<String, Object>> result = new ArrayList<>();

        Long userId = AuthContextHolder.getUserId();
        //1.获取声音的信息
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        //2.获取声音的属性
        Long albumId = trackInfo.getAlbumId();
        Integer orderNum = trackInfo.getOrderNum();
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        BigDecimal price = albumInfo.getPrice();   //如果专辑是整专辑,price为整个专辑的价格,如果是单集,price为单集声音的价格
        //3.根据专辑id查询当前用户购买过专辑的声音列表
        Result<Map<Long, String>> userPaidAlbumTrackList = userInfoFeignClient.getUserPaidAlbumTrackList(albumId, userId);
        Map<Long, String> data = userPaidAlbumTrackList.getData();
        if (data==null){
            throw new GuiguException(201,"远程调用用户微服务获取用户购买专辑声音列表失败");
        }
        //4.查询当前声音后面的所有的声音列表
        List<TrackInfo> trackInfos = trackInfoMapper.selectList(new LambdaQueryWrapper<TrackInfo>()
                .eq(TrackInfo::getAlbumId, albumId)
                .gt(TrackInfo::getOrderNum, orderNum)
                .orderByAsc(TrackInfo::getOrderNum));
        //5.当前声音后面的所有的声音的id集合
        List<Long> trackIds = trackInfos.stream().map(TrackInfo::getId).limit(50).collect(Collectors.toList());
        //6.过滤得出真正需要付费的声音列表
        List<Long> canPayTrackIds = new ArrayList<>();
        if (data!=null && data.size()==0){
            canPayTrackIds = trackIds;
        }else{
            canPayTrackIds = trackIds.stream().filter(id -> !data.containsKey(id)).collect(Collectors.toList());
        }
        //7.展示付费的列表
        //7.1展示本集
        HashMap<String, Object> currentMap = new HashMap<>();
        currentMap.put("name","本集");
        currentMap.put("price",price);
        currentMap.put("trackCount",0);
        result.add(currentMap);
        //7.2展示后面的集数
        //总集数
        int totalCount = canPayTrackIds.size();
        //计算有多少块数
        int block = totalCount % 10==0 ? totalCount / 10 : totalCount/10 + 1;
        for (int i = 1; i <=block ; i++) {
            int count = i * 10;

            if (count > totalCount) {
                HashMap<String, Object> map2 = new HashMap<>();
                map2.put("name","后"+totalCount+"集");
                map2.put("price",price.multiply(new BigDecimal(totalCount)));
                map2.put("trackCount",totalCount);
                result.add(map2);
                break;
            }

            HashMap<String, Object> map1 = new HashMap<>();
            map1.put("name","后"+count+"集");
            map1.put("price",price.multiply(new BigDecimal(count)));
            map1.put("trackCount",count);
            result.add(map1);
        }
        HashMap<String, Object> map3 = new HashMap<>();
        map3.put("name","全集");
        map3.put("price",price.multiply(new BigDecimal(totalCount+1)));
        map3.put("trackCount",totalCount+1);
        result.add(map3);


        return result;
    }

    /**
     * 处理需要付费的专辑
     * @param userId
     * @param albumId
     * @param tracksForFree
     * @param albumTrackListVoIPage
     * @param payType
     * @return
     */
    private IPage<AlbumTrackListVo> dealNeedTypeAlbum(Long userId, Long albumId, Integer tracksForFree, IPage<AlbumTrackListVo> albumTrackListVoIPage, String payType) {

        //1.判断有没有登录
        if (userId==null){
            //没有登录,2种策略,1)1集都没得看,2)返回免费的集给前端
            List<AlbumTrackListVo> collect = albumTrackListVoIPage.getRecords().stream().filter(albumTrackListVo -> albumTrackListVo.getOrderNum() <= tracksForFree).collect(Collectors.toList());
            return albumTrackListVoIPage.setRecords(collect);
        }

        //判断用户是否买过整个专辑或者专辑下的声音
        return realShowAlbumTrackListVo(userId,albumId,tracksForFree,albumTrackListVoIPage,payType);
    }


    /**
     * 处理需要Vip的专辑
     * @param userId
     * @param albumId
     * @param tracksForFree
     * @param albumTrackListVoIPage
     * @param payType
     * @return
     */
    private IPage<AlbumTrackListVo> dealVipTypeAlbum(Long userId, Long albumId, Integer tracksForFree, IPage<AlbumTrackListVo> albumTrackListVoIPage, String payType) {

        //1.判断有没有登录
        if (userId==null){
            //没有登录,2种策略,1)1集都没得看,2)返回免费的集给前端
            List<AlbumTrackListVo> collect = albumTrackListVoIPage.getRecords().stream().filter(albumTrackListVo -> albumTrackListVo.getOrderNum() <= tracksForFree).collect(Collectors.toList());
            return albumTrackListVoIPage.setRecords(collect);
        }
        //2.登录了,判断用户是否是vip
        UserInfoVo userInfoVo = userInfoFeignClient.getUserInfoByUserId(userId).getData();
        if (null==userInfoVo){
           throw new GuiguException(201,"用户信息不存在");
        }
        Integer isVip = userInfoVo.getIsVip();
        Date vipExpireTime = userInfoVo.getVipExpireTime();//vip到期时间
        //不是Vip或者是vip但是vip到期了
        if ("0".equals(isVip+"")||("1".equals(isVip+"")&&vipExpireTime.before(new Date()))){
            //免费集数之外的集数都要显示付费,直接买过的集数也要是免费的
            return realShowAlbumTrackListVo(userId,albumId,tracksForFree,albumTrackListVoIPage,payType);
        }else {
            //是Vip且vip没有到期了
            //全部都要显示为免费
            return albumTrackListVoIPage;
        }
    }

    /**
     * 真正需要付费的集数
     * @param userId
     * @param albumId
     * @param tracksForFree
     * @param albumTrackListVoIPage
     * @param payType
     * @return
     */
    private IPage<AlbumTrackListVo> realShowAlbumTrackListVo(Long userId, Long albumId, Integer tracksForFree, IPage<AlbumTrackListVo> albumTrackListVoIPage, String payType) {
       //1.判断当前登录的用户是否买过当前专辑的声音或者整个专辑
        //1.1获取当前用户购买过某个专辑下的所有声音的集合
        if ("0201".equals(payType)){
            Result<Map<Long,String>> paidAlbumTrackListMap = userInfoFeignClient.getUserPaidAlbumTrackList(albumId,userId);
            Map<Long, String> paidAlbumTrackListMapData = paidAlbumTrackListMap.getData();
            if (paidAlbumTrackListMapData==null){
                throw new GuiguException(201,"远程调用用户微服务获取用户购买专辑声音列表失败");
            }
            List<AlbumTrackListVo> realShowAlbumTrackList = albumTrackListVoIPage.getRecords().stream().map(albumTrackListVo -> {
                //2.判断当前声音是否在购买过的声音集合中
                if (StringUtils.isEmpty(paidAlbumTrackListMapData.get(albumTrackListVo.getTrackId())) && albumTrackListVo.getOrderNum() <= tracksForFree) {
                    //没有买过,并且超过免费集数,需要显示付费
                    albumTrackListVo.setIsShowPaidMark(true);
                }
                return albumTrackListVo;
            }).collect(Collectors.toList());
           return albumTrackListVoIPage.setRecords(realShowAlbumTrackList);
        }else {
            //整个专辑
            //3.判断当前用户是否买过当前专辑
           Result<Boolean>  whetherUserPaidAlbum =  userInfoFeignClient.getUserPaidAlbum(albumId,userId);
            Boolean aBoolean = whetherUserPaidAlbum.getData();
            if (aBoolean==null){
                throw new GuiguException(201,"远程调用用户微服务获取用户购买专辑失败");
            }
            //如果买过,专辑返回
            if (aBoolean){
                return albumTrackListVoIPage;
            }else {
                //如果没买过,除了免费集数之外的声音都要显示付费图标
                List<AlbumTrackListVo> collect = albumTrackListVoIPage.getRecords().stream().map(albumTrackListVo -> {
                    if (albumTrackListVo.getOrderNum() > tracksForFree) {
                        albumTrackListVo.setIsShowPaidMark(true);
                    }
                    return albumTrackListVo;
                }).collect(Collectors.toList());
                return albumTrackListVoIPage.setRecords(collect);
            }
        }

    }

    private void removeVodFiled(String mediaFileId) {
        try{
            // 1.实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密

            Credential cred = new Credential(vodProperties.getSecretId(), vodProperties.getSecretKey());

            // 2.实例化要请求产品的client对象,clientProfile是可选的
            VodClient client = new VodClient(cred, vodProperties.getRegion());
            // 3.实例化一个请求对象,每个接口都会对应一个request对象
            DeleteMediaRequest req = new DeleteMediaRequest();
            //3.1设置请求参数
            req.setFileId(mediaFileId);
            // 4.返回的resp是一个DeleteMediaResponse的实例，与请求对象对应
            DeleteMediaResponse resp = client.DeleteMedia(req);
            // 5.输出json格式的字符串回包
            System.out.println(AbstractModel.toJsonString(resp));
        } catch (TencentCloudSDKException e) {
            log.info("删除失败", e.getMessage());
            throw new GuiguException(201,"删除失败");
        }

    }

    /**
     * 获取媒体信息
     * @param mediaFileId
     * @return
     */
    public TrackMediaInfoVo getMediaInfo(String mediaFileId) {
        //创建一个对象
        TrackMediaInfoVo trackMediaInfoVo = new TrackMediaInfoVo();
        //根据文件id获取媒体信息(从vod里拿)
        try{
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(vodProperties.getSecretId(), vodProperties.getSecretKey());


            // 实例化要请求产品的client对象,clientProfile是可选的
            VodClient client = new VodClient(cred, vodProperties.getRegion());
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DescribeMediaInfosRequest req = new DescribeMediaInfosRequest();
            //给请求对象设置一个id
            String[] fileIds1 = {mediaFileId};
            req.setFileIds(fileIds1);
            // 发起请求得到响应
            DescribeMediaInfosResponse resp = client.DescribeMediaInfos(req);
            // 输出json格式的字符串回包
            System.out.println(AbstractModel.toJsonString(resp));


            MediaInfo[] mediaInfoSet = resp.getMediaInfoSet();
            if (mediaInfoSet!=null && mediaInfoSet.length>0){
                MediaInfo mediaInfo = mediaInfoSet[0];
                //获取媒体信息

                MediaMetaData metaData = mediaInfo.getMetaData();
                Long size = metaData.getSize();
                Float duration = metaData.getDuration();


                MediaBasicInfo basicInfo = mediaInfo.getBasicInfo();
                String type = basicInfo.getType();
                String mediaUrl = basicInfo.getMediaUrl();

                trackMediaInfoVo.setType(type);//声音类型
                trackMediaInfoVo.setMediaUrl(mediaUrl);//声音播放地址
                trackMediaInfoVo.setSize(size);//声音大小
                trackMediaInfoVo.setDuration(duration);//声音时长
                return trackMediaInfoVo;
            }

        } catch (TencentCloudSDKException e) {
            log.info("获取媒体信息失败",e.getMessage());

        }
        return null;
    }

        /*
在同一个类的有2个方法都加了@Transactional注解，
在第1个方法中调用第2个方法,
需要用代理对象调用第2个放法,不用代理对象会失效*/

    /**
     * 初始化歌曲统计数据
     * @param trackId
     */
    @Transactional(rollbackFor = Exception.class) //为了保证原子性，开启事务
    public void initTrackStat(Long trackId) {
        //将四种维度统计放到一个集合中
        ArrayList<String> dimensions = new ArrayList<>();
        dimensions.add(SystemConstant.TRACK_STAT_PLAY);
        dimensions.add(SystemConstant.TRACK_STAT_COLLECT);
        dimensions.add(SystemConstant.TRACK_STAT_PRAISE);
        dimensions.add(SystemConstant.TRACK_STAT_COMMENT);

        for (String dimension : dimensions) {
            TrackStat trackStat = new TrackStat();
            trackStat.setTrackId(trackId);
            trackStat.setStatType(dimension);
            trackStat.setStatNum(0);
            trackStatMapper.insert(trackStat);
        }
    }
}
